---
title: Лучшие практики и рекомендации в effector
lang: ru
description: Лучшие практики и рекомендации по эффективной работе с Effector, включая общие паттерны, типизацию, оптимизацию и популярные библиотеки экосистемы
redirectFrom:
  - /ru/docs/conventions/naming
  - /ru/conventions/naming
---

import Tabs from '@components/Tabs/Tabs.astro';
import TabItem from '@components/Tabs/TabItem.astro';

# Лучшие практики в Effector (#best-practices)

В этом разделе собраны рекомендации по эффективной работе с Effector, основанные на опыте сообщества и команды разработчиков.

## Создавайте маленькие сторы (#keep-stores-small)

В отличие от Redux, в Effector рекомендуется делать сторы максимально атомарными. Давайте разберем, почему это важно и какие преимущества это дает.

Большие сторы с множеством полей создают несколько проблем:

- Лишние ре-рендеры: При изменении любого поля обновляются все компоненты, подписанные на стор
- Тяжелые вычисления: Каждое обновление требует копирования всего объекта
- Лишние вычисления: если вы имеете производные сторы зависящие от большого стора, то они будут перевычисляться

Атомарные сторы позволяют:

- Обновлять только то, что действительно изменилось
- Подписываться только на нужные данные
- Эффективнее работать с реактивными зависимостями

```ts
// ❌ Большой стор - любое изменение вызывает обновление всего
const $bigStore = createStore({
  profile: { /* много полей */ },
  settings: { /* много полей */ },
  posts: [ /* много постов */ ]
})

// ✅ Атомарные сторы - точечные обновления
const $userName = createStore('')
const $userEmail = createStore('')
const $posts = createStore<Post[]>([])
const $settings = createStore<Settings>({})

// Компонент подписывается только на нужные данные
const UserName = () => {
  const name = useUnit($userName) // Обновляется только при изменении имени
  return <h1>{name}</h1>
}
```

Правила атомарных сторов:

- Один стор = одна ответственность
- Стор должен быть неделимым
- Сторы можно объединять через combine
- Обновление стора не должно затрагивать другие данные

## Immer для сложных объектов (#immer)

Если ваш стор содержит в себе вложенные структуры, то вы можете использовать всеми любимый [Immer](https://github.com/immerjs/immer) для упрощенного обновления:

```ts
import { createStore } from 'effector';
import { produce } from 'immer';

const $users = createStore<User[]>([]);

$users.on(userUpdated, (users, updatedUser) =>
  produce(users, (draft) => {
    const user = draft.find((u) => u.id === updatedUser.id);
    if (user) {
      user.profile.settings.theme = updatedUser.profile.settings.theme;
    }
  }),
);
```

## Явный старт приложения (#explicit-app-start)

Чтобы лучше контролировать жизненный цикл вашего приложения мы рекомендуем создавать явные [событие](/ru/api/effector/Event), например `appStarted`. Если вам необходим более гранулярный контроль, то не стесняйтесь создавать больше дополнительных [событий](/ru/api/effector/Event). Более полную информацию вы найдете на странице [Явный запуск приложения](/ru/resources/explicit-start).

```ts
export const appStarted = createEvent();
```

## Используйте `scope` (#use-scope)

Команда effector рекомендует всегда использовать `Scope`, даже если ваше приложение не использует SSR.
Это необходимо, чтобы в будущем вы могли спокойно мигрировать на режим работы со `Scope`.

## Хук `useUnit` (#use-unit)

Использование хука `useUnit` является рекомендуемым способом для работы с юнитами при использовании фреймворков (📘React, 📗Vue и 📘Solid).
Почему нужно использовать `useUnit`:

- Корректная работа со сторами
- Оптимизированные обновления
- Автоматическая [работа со `Scope`](/ru/advanced/work-with-scope) – юниты сами знают в каком скоупе они были вызваны

## Чистые функции (#pure-functions)

Используйте чистые функции везде, кроме [эффектов](/ru/api/effector/Effect), для обработки данных, это обеспечивает:

- Детерминированный результат
- Отсутствие сайд-эффектов
- Проще для тестирования
- Легче поддерживать

:::tip{title="Эта работа для эффектов"}
Если ваш код может выбросить ошибку или может закончится успехом/неуспехом - то это отличное место для эффектов.
:::

## Отладка (#debug)

Мы настоятельно рекомендуем вам использовать библиотеку [`patronum`](https://patronum.effector.dev/operators/) и метод [`debug`](https://patronum.effector.dev/operators/debug/).

```ts
import { createStore, createEvent, createEffect } from 'effector';
import { debug } from 'patronum/debug';

const event = createEvent();
const effect = createEffect().use((payload) => Promise.resolve('result' + payload));
const $store = createStore(0)
  .on(event, (state, value) => state + value)
  .on(effect.done, (state) => state * 10);

debug($store, event, effect);

event(5);
effect('demo');

// => [store] $store 1
// => [event] event 5
// => [store] $store 6
// => [effect] effect demo
// => [effect] effect.done {"params":"demo", "result": "resultdemo"}
// => [store] $store 60
```

Однако вам никто не запрещает использовать `.watch` или [`createWatch`](/ru/api/effector/createWatch) для отладки.

## Фабрики (#factories)

Создание фабрик это частый паттерн при работе с effector, он облегчает использование однотипного кода. Однако вы можете столкнуться с [проблемой одинаковых sid](/ru/explanation/sids/#unique-sids), которые могу помешать при работе с SSR.

Чтобы избежать этой проблемы, мы рекомендуем использовать библиотеку [@withease/factories](https://withease.effector.dev/factories/).

Если если ваша среда не позволяет добавлять дополнительные зависимости, то вы можете создать свою собственную фабрику следуя [этим указаниями](/ru/explanation/sids/#custom-factories).

## Работа с сетью (#work-with-network)

Для удобной работы effector с запросами по сети вы можете использовать [farfetched](https://ff.effector.dev/).

Farfetched предоставляет:

- Мутации и квери
- Готовое апи для кеширование и др.
- Независимость от фреймворков

## Утилиты для работы с effector (#effector-utils)

В экосистеме Effector находится библиотека [patronum](https://patronum.effector.dev/operators/), которая предоставляет готовые решения для работы с юнитами:

- Управление состоянием (`condition`, `status` и др.)
- Работа со временем (`debounce`, `interval` и др.)
- Функции предикаты (`not`, `or`, `once` и др.)

## Упрощение сложной логики с `createAction` (#create-action)

[`effector-action`](https://github.com/AlexeyDuybo/effector-action) - это библиотека, которая позволяет писать императивный код для сложной условной логики, сохраняя при этом декларативную природу effector.
При этом `effector-action` помогает сделать ваш код более читабельным:

<Tabs>
  <TabItem label="❌ Сложный sample">

```ts
import { sample } from 'effector';

sample({
  clock: formSubmitted,
  source: {
    form: $form,
    settings: $settings,
    user: $user,
  },
  filter: ({ form }) => form.isValid,
  fn: ({ form, settings, user }) => ({
    data: form,
    theme: settings.theme,
  }),
  target: submitFormFx,
});

sample({
  clock: formSubmitted,
  source: $form,
  filter: (form) => !form.isValid,
  target: showErrorMessageFx,
});

sample({
  clock: submitFormFx.done,
  source: $settings,
  filter: (settings) => settings.sendNotifications,
  target: sendNotificationFx,
});
```

  </TabItem>

<TabItem label="✅ С createAction">

```ts
import { createAction } from 'effector-action';

const submitForm = createAction({
  source: {
    form: $form,
    settings: $settings,
    user: $user,
  },
  target: {
    submitFormFx,
    showErrorMessageFx,
    sendNotificationFx,
  },
  fn: (target, { form, settings, user }) => {
    if (!form.isValid) {
      target.showErrorMessageFx(form.errors);
      return;
    }

    target.submitFormFx({
      data: form,
      theme: settings.theme,
    });
  },
});

createAction(submitFormFx.done, {
  source: $settings,
  target: sendNotificationFx,
  fn: (sendNotification, settings) => {
    if (settings.sendNotifications) {
      sendNotification();
    }
  },
});

submitForm();
```

  </TabItem>
</Tabs>

## Именование (#naming)

Используйте принятые соглашения об именовании:

- Для сторов – префикс `$`
- Для эффектов – постфикс `fx`, это позволит вам отличать ваши эффекты от событий
- Для событий – правил нет, однако мы предлагаем вам называть события, которые напрямую запускают обновления сторов, как будто они уже произошли.

```ts
const updateUserNameFx = createEffect(() => {});

const userNameUpdated = createEvent();

const $userName = createStore('JS');

$userName.on(userNameUpdated, (_, newName) => newName);

userNameUpdated('TS');
```

:::info{title="Соглашение об именовании"}
Выбор между префиксом или постфиксом в основном является вопросом личных предпочтений. Это необходимо для улучшения опыта поиска в вашей IDE.
:::

## Антипаттерны (#anti-patterns)

### Использование watch для логики (#watch-for-logic)

`watch` следует использовать только для отладки.

<Tabs>
  <TabItem label="❌ Неправильно">

```ts
// Логика в watch
$user.watch((user) => {
  localStorage.setItem('user', JSON.stringify(user));
  api.trackUserUpdate(user);
  someEvent(user.id);
});
```

  </TabItem>
  <TabItem label="✅ Правильно">

```ts
// Отдельные эффекты для сайд-эффектов
const saveToStorageFx = createEffect((user: User) =>
  localStorage.setItem('user', JSON.stringify(user)),
);

const trackUpdateFx = createEffect((user: User) => api.trackUserUpdate(user));

// Связываем через sample
sample({
  clock: $user,
  target: [saveToStorageFx, trackUpdateFx],
});

// Для событий тоже используем sample
sample({
  clock: $user,
  fn: (user) => user.id,
  target: someEvent,
});
```

</TabItem>
</Tabs>

### Сложные вложенные sample (#complex-samples)

Избегайте сложных и вложенных цепочек `sample`.

### Абстрактные названия в колбеках (#naming-in-callbacks)

Используйте осмысленные имена вместо абстрактных `value`, `data`, `item`.

<Tabs>
  <TabItem label="❌ Неправильно">

```ts
$users.on(userAdded, (state, payload) => [...state, payload]);

sample({
  clock: buttonClicked,
  source: $data,
  fn: (data) => data,
  target: someFx,
});
```

  </TabItem>
  <TabItem label="✅ Правильно">

```ts
$users.on(userAdded, (users, newUser) => [...users, newUser]);

sample({
  clock: buttonClicked,
  source: $userData,
  fn: (userData) => userData,
  target: updateUserFx,
});
```

  </TabItem>
</Tabs>

### Императивные вызовы в эффектах (#imperative-calls)

Не вызывайте события или эффекты императивно внутри других эффектов, вместо этого используйте декларативный стиль.

<Tabs>
  <TabItem label="❌ Неправильно">

```ts
const loginFx = createEffect(async (params) => {
  const user = await api.login(params);

  // Императивные вызовы
  setUser(user);
  redirectFx('/dashboard');
  showNotification('Welcome!');

  return user;
});
```

  </TabItem>
  <TabItem label="✅ Правильно">

```ts
const loginFx = createEffect((params) => api.login(params));
// Связываем через sample
sample({
  clock: loginFx.doneData,
  target: [
    $user, // Обновляем стор
    redirectToDashboardFx,
    showWelcomeNotificationFx,
  ],
});
```

 </TabItem>
</Tabs>

### Использование getState (#get-state)

Не используйте `$store.getState` для получения значений. Если вам нужно получить данные какого-то стора, то передайте его туда, например в `source` у `sample`:

<Tabs>
  <TabItem label="❌ Неправильно">

```ts
const submitFormFx = createEffect((formData) => {
  // Получаем значения через getState
  const user = $user.getState();
  const settings = $settings.getState();

  return api.submit({
    ...formData,
    userId: user.id,
    theme: settings.theme,
  });
});
```

</TabItem>
  <TabItem label="✅ Правильно">

```ts
// Получаем значения через параметры
const submitFormFx = createEffect(({ form, userId, theme }) => {});

// Получаем все необходимые данные через sample
sample({
  clock: formSubmitted,
  source: {
    form: $form,
    user: $user,
    settings: $settings,
  },
  fn: ({ form, user, settings }) => ({
    form,
    userId: user.id,
    theme: settings.theme,
  }),
  target: submitFormFx,
});
```

  </TabItem>
</Tabs>

### Бизнес-логика в UI (#business-logic-in-ui)

Не тащите вашу логику в UI элементы, это [основная философия](/ru/introduction/motivation) effector и то, от чего effector пытается избавить вас, а именно зависимость логики от UI.

### Создание юнитов в рантайме (#create-units-in-runtime)

Никогда не создавайте юниты и связи между ними в рантайме или другим динамическим образом. Корректная [инициализация юнитов](/ru/resources/static-initialization) является статичным образом на уровне модуля.

Кратко об антипаттернах:

1. Не используйте `watch` для логики, только для отладки
2. Избегайте прямых мутаций в сторах
3. Не создавайте сложные вложенные `sample`, их сложно читать
4. Не используйте большие сторы, используйте атомарный подход
5. Используйте осмысленные названия параметров, а не абстрактные
6. Не вызывайте события внутри эффектов императивно
7. Не используйте `$store.getState` для работы
8. Не тащите логику в UI
9. Не создавайте юниты в рантайме
